#include "Latexcon.h"
#include <stdexcept>
#include <iostream>
#include <vector>
#include <cstdio>
#include <locale>
#include <fstream>
#include <Tools.h>

#include <boost/filesystem.hpp>
#include <boost/process.hpp>
#include <boost/program_options/detail/utf8_codecvt_facet.hpp>
#include <boost/bind.hpp>

#include "utf8.h"

HtmlReader::HtmlReader(std::string theFile)
{
	FILE *file = fopen(theFile.c_str(),"r");
	char *data = NULL;
	unsigned int len=0;

	fseek(file, 0, SEEK_END);
	len=ftell(file);
	fseek(file, 0, SEEK_SET);

	data = new char[len];
	fclose(file);

	htmlParserCtxtPtr parser = htmlCreateMemoryParserCtxt(data,len);
	myHtmlDoc = htmlCtxtReadFile(parser,theFile.c_str(),"UTF-8",HTML_PARSE_NOERROR);
	delete[] data;
}

HtmlReader::~HtmlReader(void)
{
	if(myHtmlDoc)
		xmlFreeDoc(myHtmlDoc);

}


bool HtmlReader::StartParsing()
{
	if(myHtmlDoc)
	{
		walk_tree(xmlDocGetRootElement(myHtmlDoc));
		return true;
	}

	return false;
}


bool HtmlReader::Save(std::string theOutput)
{
	if(myHtmlDoc)
	{
		int res = htmlSaveFileEnc(theOutput.c_str(),myHtmlDoc,"UTF-8");
		if(res > 0)
		{
			postprocess(theOutput);
			return true;
		}
		else
			return false;
	}

	return false;
}


void HtmlReader::walk_tree(xmlNode *theNode)
{
	xmlNode *actualNode = NULL;

	for(actualNode = theNode; actualNode ; actualNode = actualNode->next)
	{
		if(actualNode->content != NULL)
		{
			char *p_res = *OnTextParsed(actualNode->name,actualNode->content);
			if(p_res!=NULL)
			{
				actualNode->content = xmlCharStrdup(p_res);
			}
		}
		walk_tree(actualNode->children);
	}
}


void HtmlReader::postprocess(std::string theFile)
{
	std::wstring _wholepage;
	std::wifstream aFile;

	std::locale _utf8loc(std::locale(),new boost::program_options::detail::utf8_codecvt_facet());
	aFile.imbue(_utf8loc);

	aFile.open(theFile.c_str());
	if(aFile.is_open())
	{
		size_t _length = 0; 
		aFile.seekg(0,std::ios::end);
		_length = aFile.tellg();
		aFile.seekg(0,std::ios::beg);

		wchar_t *p_buffer = new wchar_t[_length];
		aFile.read(p_buffer,_length);
		_wholepage.append(p_buffer,_length);

		delete[] p_buffer;
		aFile.close();

		Tools::ReplaceAll<std::wstring>(_wholepage,L"&lt;",L"<");
		Tools::ReplaceAll<std::wstring>(_wholepage,L"&gt;",L">");

		std::wofstream aOutput(theFile.c_str(),std::ios::out);
		if(aOutput.is_open())
		{
			aOutput.write(_wholepage.c_str(),_wholepage.size());
			aOutput.close();
		}
	}
}




Latexcon::Latexcon(void)
{
	std::string aExecName;

#ifdef _WIN32
	aExecName = "latex.exe";
#else
	aExecName = "latex";
#endif

	boost::process::self &mySelf = boost::process::self::get_instance();
	boost::process::environment env = mySelf.get_environment();
	for(boost::process::environment::iterator it = env.begin(); it != env.end(); ++it)
	{
		if (it->first == "plag_plugin_path")
		{
			myPath = it->second;
#ifdef _WIN32
			myTmpDir = it->second + "tmp_tex\\";
#else
			myTmpDir = it->second + "tmp_tex/";
#endif
			boost::filesystem::create_directory(myTmpDir);
		}
	}

	if(myPath.empty())
	{
		std::cerr << "Cant find the enviroment variable plag_plugin_path, please set it first.." << std::endl;
		throw std::runtime_error("no enviroment variable");
	}

	myExe = myPath + aExecName;

	if(!boost::filesystem::exists(myExe))
	{
		throw std::runtime_error("Cant find :" +myExe);
	}
}

Latexcon::~Latexcon(void)
{
	/*try
	{
		boost::filesystem::remove_all(myTmpDir);
	}catch(...)
	{
		std::cerr << "Cant clean up temporary files." << std::endl;
	}*/
}

bool Latexcon::Open(PComunicator::PFileInfo &theFile)
{
	theFile.Save(myTmpDir);
	myTexFile = myTmpDir + theFile.GetFullName();
	
	myHtml.clear();
	make_html();

	return true;
}


std::string Latexcon::Format()
{
	return "tex";
}

char *Latexcon::Autor()
{
	return "Marek Szanyi";
}

char *Latexcon::Version()
{
	return "1.0";
}

void Latexcon::make_html()
{
	boost::process::context ctx;
	ctx.stdout_behavior = boost::process::silence_stream();
	ctx.stderr_behavior = boost::process::silence_stream();

	std::string cmd = myExe +" "+myTexFile;

	boost::process::child child = boost::process::launch_shell(cmd,ctx);
	boost::process::status stat = child.wait();

	myHtml = myTexFile;
	Tools::Replace<std::string>(myHtml,".tex",".html");
}

std::string Latexcon::CreateHtml(std::string thePath)
{
	if(!boost::filesystem::exists(thePath))
	{
		boost::filesystem::create_directories(thePath);
	}

	if(myHtml.empty())
	{
		return myHtml;
	}
	else
	{
		thePath += "latexdoc.html";
		try
		{
			boost::filesystem::copy_file(myHtml,thePath);
		}
		catch (std::exception &ex)
		{
			std::cerr << ex.what() << std::endl;
			return thePath;
		}
			
		return thePath;
	}
}


std::wstring Latexcon::Read()
{
	if(myHtml.empty())
	{
		std::wstring empty;
		return empty;
	}

	myContent.clear();
	HtmlReader aReader(myHtml);
	aReader.OnTextParsed.connect(boost::bind(&Latexcon::parsed_text,this,_1,_2));
	aReader.StartParsing();
	
	return myContent;
}


char *Latexcon::parsed_text(const unsigned char *node, unsigned char *nodeValue)
{
	if(node == NULL)
		return NULL;

	std::string _nodeName(reinterpret_cast<const char*>(node));
	if(_nodeName == "TITLE" || _nodeName == "title")
		return NULL;

	if(_nodeName != "text")
		return NULL;

	if(nodeValue == NULL || nodeValue[0] <= 13)
		return NULL;

	std::string _valueUTF8(reinterpret_cast<char*>(nodeValue));
	std::wstring _text;
	utf8::utf8to16(_valueUTF8.begin(),_valueUTF8.end(),back_inserter(_text));
	
	myContent.append(_text);
	return NULL;
}